using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace BlockOS
{
    public static class CoordConvert
    {
        /* There are 3 kinds of coordinates:
         * 1. world position: the unity coordinates, which is the most percise.
         * 2. block position: an 3d index of a block.
         * 3. chunk position: an 3d index of a group of blocks.
         */

        public static Vector3Int CalBlockPos(Vector3 worldPos, Vector3 surfaceNormal)
        {
            surfaceNormal.Normalize();
            worldPos -= 0.1f * surfaceNormal;
            return ToBlockPos(worldPos);
        }

        public static Vector3Int ToBlockPos(this Vector3 worldPos) => new(Mathf.FloorToInt(worldPos.x), Mathf.FloorToInt(worldPos.y), Mathf.FloorToInt(worldPos.z));

        public static int ManhattanDistance(Vector3Int a, Vector3Int b)
        {
            return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y) + Mathf.Abs(a.z - b.z);
        }

        public static Vector3Int ToChunkPos(this Vector3 worldPos) => new(
            Mathf.FloorToInt(worldPos.x / Consts.Chunk.SIZE),
            Mathf.FloorToInt(worldPos.y / Consts.Chunk.SIZE),
            Mathf.FloorToInt(worldPos.z / Consts.Chunk.SIZE));

        public static Vector3Int ChunkPosToBlockPos(Vector3Int chunkPos)
        {
            return chunkPos * Consts.Chunk.SIZE;
        }

        public static Vector3Int BlockPosToChunkPos(this Vector3Int blockPos) => BlockPosToChunkPos(blockPos.x, blockPos.y, blockPos.z);

        public static Vector3Int BlockPosToChunkPos(int x, int y, int z)
        {
            return new Vector3Int(
                Mathf.FloorToInt(x / (float)Consts.Chunk.SIZE),
                Mathf.FloorToInt(y / (float)Consts.Chunk.SIZE),
                Mathf.FloorToInt(z / (float)Consts.Chunk.SIZE)
            );
        }

        public static Vector3Int BlockPos2InternalPos(Vector3Int blockPos)
        {
            blockPos.x = blockPos.x >= 0 ? blockPos.x : Mathf.Abs(Consts.Chunk.SIZE - Mathf.Abs(blockPos.x) % Consts.Chunk.SIZE);
            blockPos.y = blockPos.y >= 0 ? blockPos.y : Mathf.Abs(Consts.Chunk.SIZE - Mathf.Abs(blockPos.y) % Consts.Chunk.SIZE);
            blockPos.z = blockPos.z >= 0 ? blockPos.z : Mathf.Abs(Consts.Chunk.SIZE - Mathf.Abs(blockPos.z) % Consts.Chunk.SIZE);
            return new Vector3Int(blockPos.x % Consts.Chunk.SIZE, blockPos.y % Consts.Chunk.SIZE, blockPos.z % Consts.Chunk.SIZE);
        }

        public static Vector3Int InternalPos2BlockPos(int x, int y, int z, Vector3Int chunkPos)
        {
            var ans = ChunkPosToBlockPos(chunkPos);
            ans.x += x;
            ans.y += y;
            ans.z += z;
            return ans;
        }

        public static int InternalPos2BlockPos(int iPos, int chunkPos) => chunkPos * Consts.Chunk.SIZE + iPos;
    }

}
